package com.blfq.server;

import com.blfq.common.util.DateUtils;
import com.blfq.common.util.loadBanner;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;

import static com.blfq.common.constant.Constants.SERVER_PORT;
import static com.blfq.common.constant.Constants.THREAD_COUNT;

public class ChatServer {

    /**
     * 使用用户账号保存聊天用户列表，并管理用户的socket连接
     * 思考：能够同时保存用户的登录时间，IP地址等聊天需要用的基本信息?
     */
    private static Map<String, Socket> clients = new ConcurrentHashMap<>();

    /**
     *  服务器处理客户端的线程池，默认为50个线程，
     *  思考：能否使用配置文件进行配置，如何实现？
     *  常量类，已解决
     */
    private static ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {
            loadBanner.printBanner();
            System.out.println(DateUtils.getTime()+"[DEBUG]聊天服务器已启动...");

            while (true) {
                Socket clientSocket = serverSocket.accept();
                pool.execute(new ClientHandler(clientSocket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 服务器处理聊天信息的线程类，内部类
     *
     */
    private static class ClientHandler implements Runnable {
        /**
         * 客户端的socket连接
         */
        private Socket clientSocket;

        /**
         * 客户的账号
         */
        private String clientName;

        public ClientHandler(Socket socket) {
            this.clientSocket = socket;
        }

        @Override
        public void run() {
            //使用try -with resouce方式打开网络流
            try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                 PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
                //处理用户登录信息
                clientName = in.readLine();
                clients.put(clientName, clientSocket);
                //信息转发到所有在线的用户，有新用户登录
                //去除/符号
                String IP=clientSocket.getInetAddress().toString().replace("/","");
                String message="登录IP:"+IP+"["+clientName + "]现已加入聊天室!";
                broadcastMessage(" 服务器",message );
                //处理当前用户发送的信息
                while ((message = in.readLine()) != null) {
                    broadcastMessage(clientName, message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (clientName != null) {
                    //当程序异常，从聊天列表中删除当前登录账号
                    clients.remove(clientName);
                    //并广播到其它在线用户
                    broadcastMessage(" 服务器", "["+clientName + "]已离开聊天室");
                }
            }
        }

        private void broadcastMessage(String sender, String message) {
            //使用Lambda表达式简化代码
            //遍历在线用户，转发message
            clients.forEach((name, socket) -> {
                try {
                    //使用打印流向客户端发送message
                    PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                    //思考：返回消息是，能否把客户端的ip地址显示处理
                    out.println(DateUtils.getTime()+" "+sender + ": " + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}